package com.easylinkin.linkappapi.facerecognition.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.easylinkin.linkappapi.deviceunit.service.DeviceUnitService;
import com.easylinkin.linkappapi.facerecognition.config.AiPlatformConfig;
import com.easylinkin.linkappapi.facerecognition.config.OssClientConfig;
import com.easylinkin.linkappapi.facerecognition.entity.FaceCamera;
import com.easylinkin.linkappapi.facerecognition.entity.FacePhoto;
import com.easylinkin.linkappapi.facerecognition.entity.Personnel;
import com.easylinkin.linkappapi.facerecognition.entity.PersonnelIdentifier;
import com.easylinkin.linkappapi.facerecognition.entity.RecognitionRecord;
import com.easylinkin.linkappapi.facerecognition.service.ai.FaceRecognitionService;
import com.easylinkin.linkappapi.facerecognition.service.oss.ObjectStorageOutsideService;
import com.easylinkin.linkappapi.facerecognition.util.Base64Util;
import com.easylinkin.linkappapi.facerecognition.util.OssImageUrlToByteArrayUtil;
import com.easylinkin.linkappapi.facerecognition.util.SpringContextUtil;
import com.easylinkin.linkappapi.openapi.dto.DatapushDTO;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;

public class FaceRecognitionTask implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(FaceRecognitionTask.class);

    // AEP接收到的原始数据
    private DatapushDTO originalData;

    private static final FaceCameraService faceCameraService = SpringContextUtil.getBean(FaceCameraService.class);

    private static final ObjectStorageOutsideService ossClient = SpringContextUtil.getBean("oss", ObjectStorageOutsideService.class);

//    private static final ObjectStorageInternalService ossClient = SpringContextUtil.getBean("ossInternal", ObjectStorageInternalService.class);

    private static final OssClientConfig ossClientConfig = SpringContextUtil.getBean(OssClientConfig.class);

    private static final AiPlatformConfig aiPlatformConfig = SpringContextUtil.getBean(AiPlatformConfig.class);

    private static final FaceRecognitionService faceRecognitionService = SpringContextUtil.getBean(FaceRecognitionService.class);

    private static final PersonnelService personnelService = SpringContextUtil.getBean(PersonnelService.class);

    private static final PersonnelIdentifierService personnelIdentifierService = SpringContextUtil.getBean(PersonnelIdentifierService.class);

    private static final RecognitionRecordService recognitionRecordService = SpringContextUtil.getBean(RecognitionRecordService.class);

    private static final FacePhotoService facePhotoService = SpringContextUtil.getBean(FacePhotoService.class);

    private static final DeviceUnitService deviceUnitService = SpringContextUtil.getBean(DeviceUnitService.class);

    public FaceRecognitionTask(DatapushDTO originalData) {
        this.originalData = originalData;
    }

    public static void main(String[] args) {
        List<FaceCamera> list = new ArrayList<>();
        List<String> ss = list.stream().map(FaceCamera::getDeviceCode).collect(Collectors.toList());
        System.out.println(ss);
    }

    @Override
    public void run() {
        // 获取满足设备编号的摄像头设备
        QueryWrapper<FaceCamera> qw = new QueryWrapper<>();
        qw.eq("device_code", originalData.getDevice_id());
        if (faceCameraService.list(qw).size() > 0) {
//            如果是越界抓拍的 保存一条越界抓拍记录 结束方法
            if(checkOutOfBoundsAndSaveRecord()){
                return;
            }
            // 1. 将照片上传到OSS
            String imgData = "";
            try {
                String imgUrl = getImageUrl(originalData);
                if (StringUtils.isEmpty(imgUrl)) {
                    return;
                }
                imgData = Base64Util.encode(OssImageUrlToByteArrayUtil.imageUrlToByteArray(imgUrl));

            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
            String fileName = System.getProperty("java.io.tmpdir") + File.separator + "/img/" + System.currentTimeMillis() + ".jpeg";
            File photo = new File(fileName);
            if (!photo.getParentFile().exists()) {
                photo.getParentFile().mkdirs();
            }
            if (!base64StrToImage(imgData, photo)) {
                LOGGER.error("data transfer to image error {}", imgData);
                return;
            }

            String capturePhotoPath = "";
            String photoPath = photoPath();
            try {
                ossClient.uploadImgObject(ossClientConfig.getBucket(), photoPath, new FileInputStream(photo));
                capturePhotoPath = ossClient.generatePresignedUrl(ossClientConfig.getBucket(), photoPath, 7 * 24 * 60 * 60);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }

            // 2. 对照片进行识别
            Optional<JsonObject> recognizeResponse = faceRecognitionService.recognize(photo);

            // 删除临时生成的文件
            photo.delete();

            if (!recognizeResponse.isPresent()) {
                return;
            }

            // 3. 保存识别记录
            RecognitionRecord record = new RecognitionRecord();
            long personnelId = -1;
            if ("koala".equalsIgnoreCase(aiPlatformConfig.getPlatform()) && recognizeResponse.get().getAsJsonObject("person") != null) {
                int subjectId = recognizeResponse.get().getAsJsonObject("person").get("subject_id").getAsInt();
                int photoId = recognizeResponse.get().getAsJsonObject("person").get("photo_id").getAsInt();

                Personnel personnel = personnelService.queryPersonnelBySubjectId(String.valueOf(subjectId));

                if (personnel != null) {
                    personnelId = personnel.getId();
                    record.setName(personnel.getName());
                    PersonnelIdentifier personnelIdentifier = personnelIdentifierService.getById(personnel.getIdentifierId());
                    record.setIdentifierName(personnelIdentifier.getIdentifier());
                    record.setColor(personnelIdentifier.getColor());
                }

                FacePhoto facePhoto = facePhotoService.queryPhotoWithId(String.valueOf(photoId));
                if (facePhoto != null) {
                    record.setRecognitionPhoto(facePhoto.getPhotoUrl());
                }
            } else if ("baidu".equalsIgnoreCase(aiPlatformConfig.getPlatform()) && recognizeResponse.get().getAsJsonObject("result") != null) {
                JsonArray userList = recognizeResponse.get().getAsJsonObject("result").getAsJsonArray("user_list");
                String photoId = recognizeResponse.get().getAsJsonObject("result").get("face_token").getAsString();
                String subjectId = "";
                if (userList.size() > 0) {
                    subjectId = userList.get(0).getAsJsonObject().get("user_id").getAsString();
                }

                Personnel personnel = personnelService.queryPersonnelBySubjectId(subjectId);

                if (personnel != null) {
                    personnelId = personnel.getId();
                    record.setName(personnel.getName());
                    PersonnelIdentifier personnelIdentifier = personnelIdentifierService.getById(personnel.getIdentifierId());
                    record.setIdentifierName(personnelIdentifier.getIdentifier());
                    record.setColor(personnelIdentifier.getColor());
                }

                FacePhoto facePhoto = facePhotoService.queryPhotoWithId(String.valueOf(photoId));
                if (facePhoto != null) {
                    record.setRecognitionPhoto(facePhoto.getPhotoUrl());
                }
            }

            FaceCamera faceCamera = faceCameraService.queryCameraWithDeviceCodeWithoutTenantId(originalData.getDevice_id());

            record.setRecognitionType(0);
            record.setDeviceCode(faceCamera.getPosition()); // 抓拍设备存储摄像头的按照位置
            record.setDeviceId(faceCamera.getDeviceCode());
            record.setPersonnelId(personnelId);
            record.setCapturePhoto(capturePhotoPath);
            record.setTenantId(faceCamera.getTenantId());
            recognitionRecordService.save(record);
        }
    }

    /**
     * 根据 imageUrl类型来查找 对应的图片属性key 从而找到相应的 图片url
     * 有的 叫 image 有的叫imageFile 但类型都会是imageUrl，并且只有一个该类型的属性
     * @param originalData 推送过来的流水数据
     * @return 图片url
     */
    private String getImageUrl(DatapushDTO originalData) {
        if (originalData == null || originalData.getData() == null) {
            return null;
        }
        String physicsModel = deviceUnitService.selectPhysicsModelByDeviceCode(originalData.getDevice_id());
        JSONObject json = JSON.parseObject(physicsModel);
        Optional<String> imgIdentifier = json.getJSONArray("properties").stream().filter(p -> {
            JSONObject jp = (JSONObject) p;
            return "imageUrl".equalsIgnoreCase(jp.getJSONObject("dataType").getString("type"));
        }).map(p -> {
            JSONObject jp = (JSONObject) p;
            return jp.getString("identifier");
        }).findAny();

        if (!imgIdentifier.isPresent()) {
            return null;
        }

        JSONObject data = originalData.getData();
        return data.getString(imgIdentifier.get());
    }

    /**
     * 判断越界抓拍的并保存一条越界抓拍记录
     *
     * @return 是否为越界抓拍
     */
    private boolean checkOutOfBoundsAndSaveRecord() {
        JSONObject dataJsonObject;
        try {
            dataJsonObject = originalData.getData() != null ? ((JSONObject) originalData.getData()) : null;
        } catch (Exception e) {
            LOGGER.info("originalData.getData() 转JSONOBJECT失败");
            return false;
        }
//        判断是越界抓拍类型
        if (dataJsonObject == null || dataJsonObject.get("imageType") == null || !"1".equals(dataJsonObject.get("imageType").toString())) {
            LOGGER.info("dataJsonObject == null || faceOfClock == null || deviceID == null");
            return false;
        }
        RecognitionRecord record = new RecognitionRecord();
        record.setRecognitionType(1);
        FaceCamera faceCamera = faceCameraService.queryCameraWithDeviceCodeWithoutTenantId(originalData.getDevice_id());
        record.setDeviceCode(faceCamera.getPosition()); // 抓拍设备存储摄像头的按照位置
        record.setDeviceId(faceCamera.getDeviceCode());
//        if (dataJsonObject != null && dataJsonObject.get("imageFile") != null) {
//            record.setCapturePhoto(dataJsonObject.get("imageFile").toString());
//        }
        record.setCapturePhoto(getImageUrl(originalData));
        record.setTenantId(faceCamera.getTenantId());
        record.setPersonnelId(-1L);
        recognitionRecordService.save(record);
        return true;
    }


    /**
     * 返回抓拍照片在OSS中的存储路径
     */
    private String photoPath() {
        String path = "";
        if (ossClientConfig.getDir().endsWith("/")) {
            path += ossClientConfig.getDir() + "img/" + System.currentTimeMillis() + ".jpeg";
        } else {
            path += ossClientConfig.getDir() + "/img/" + System.currentTimeMillis() + ".jpeg";
        }

        return path;
    }

    private boolean base64StrToImage(String imgStr, File photo) {
        if (imgStr == null) {
            return false;
        }
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // 解密
            byte[] b = decoder.decodeBuffer(imgStr);
            // 处理数据
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {
                    b[i] += 256;
                }
            }

            OutputStream out = new FileOutputStream(photo);
            out.write(b);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
